--- @class Entry
--- @field lines string[]
--- @field ft string

--- @type Entry
local default_entry = {
	lines = {},
	ft = 'plaintext',
}

local items = {
	default_entry,
	default_entry,
};

--- @param entry Entry
local create_scratch_buffer = function(entry)
	local bufnr = vim.api.nvim_create_buf(false, true);

	vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, entry.lines);
	vim.keymap.set(
		'n',
		'q',
		function()
			vim.cmd('tabclose!');
		end,
		{ buffer = bufnr, noremap = true, silent = true }
	);

	vim.bo[bufnr].bufhidden = 'wipe';
	vim.bo[bufnr].swapfile = false;
	vim.bo[bufnr].buftype = 'nofile';
	vim.bo[bufnr].readonly = true;
	vim.bo[bufnr].swapfile = false;
	vim.bo[bufnr].modifiable = false;
	vim.bo[bufnr].filetype = entry.ft;

	return bufnr;
end

local function show_diff()
	local bufnr_l = create_scratch_buffer(items[1]);
	local bufnr_r = create_scratch_buffer(items[2]);

	vim.cmd('vsp');
	vim.api.nvim_win_set_buf(0, bufnr_l);
	vim.cmd(vim.api.nvim_replace_termcodes("normal <C-w>T", true, true, true));
	vim.cmd('diffthis');
	vim.cmd('vsp');
	vim.api.nvim_win_set_buf(0, bufnr_r);
	vim.cmd('diffthis');
end

local function get_visual_selection()
	-- Get start/end as (row, col)
	local start_pos            = vim.fn.getpos("'<")
	local end_pos              = vim.fn.getpos("'>")

	local start_row, start_col = start_pos[2], start_pos[3]
	local end_row, end_col     = end_pos[2], end_pos[3]

	-- Fetch the text
	local lines                = vim.api.nvim_buf_get_lines(0, start_row - 1, end_row, false)

	-- Trim the last line’s end column
	if #lines > 0 then
		lines[#lines] = string.sub(lines[#lines], 1, end_col)
		lines[1]      = string.sub(lines[1], start_col)
	end

	return lines
end

local function set_diff_entry()
	vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes("<Esc>", true, false, true), "n", false)
	vim.schedule(function()
		local selected_lines = get_visual_selection();
		local ft = vim.bo.filetype;
		items[2] = items[1];
		items[1] = {
			lines = selected_lines,
			ft = ft,
		}
	end)
end

vim.keymap.set('n', '<leader>diff<CR>', show_diff, { noremap = true, silent = true });
vim.keymap.set('v', 'D', set_diff_entry, { noremap = true, silent = true });
